var fs = require('fs');
var path = require("path");

/**
 * @param {string} absPath
 * @return {[]string}
 */
var read_file_to_string = function(absPath){
	var readret = fs.readFileSync(absPath,'utf-8');
	if (readret == null){
		return null;
	}
	else{
		var readretArray = readret.split('\n');
		var realRet = [];
		for (var i = 0; i < readretArray.length; i++){
			//考虑到数据可能是windows平台上保存的然后上传
			if (readretArray[i].length != 0){
				if (readretArray[i][readretArray[i].length - 1] == '\r'){
					readretArray[i] = readretArray[i].substring(0, readretArray[i].length - 1);
				}
				if (readretArray[i].length != 0) {
					realRet.push(readretArray[i]);				
				}
			}
		}
		return realRet;
	}
}

/**
 * @param {string, string, bool} absPath, data
 * @return {}
 */
var write_string_to_file_appendOrTruncated = function(absPath, data, isAppend){
	if (isAppend){
		fs.appendFileSync(absPath, data);
	}
	else{
		fs.writeFileSync(absPath, data);
	}
}

//testinclude_js.js
var raw = [];
var t = new Array(50002 << 2);
var lazy = new Array(50002 << 2);
var rIndex;

var Pushup = function(k) {        //更新函数，这里是实现最大值 ，同理可以变成，最小值，区间和等
	t[k] = t[k << 1] + t[k << 1 | 1];
}

//递归方式建树 build(1,1,n);
//此函数仅在main调用一次, 线段树的结构在pushup过程中不变
var build = function(k, l, r) {    //k为当前需要建立的结点，l为当前需要建立区间的左端点，r则为右端点
	if (l == r)    //左端点等于右端点，即为叶子节点，直接赋值即可
		t[k] = raw[l];
	else {
		var m = l + ((r - l) >> 1);    //m则为中间点，左儿子的结点区间为[l,m],右儿子的结点区间为[m+1,r]
		build(k << 1, l, m);    //递归构造左儿子结点
		build(k << 1 | 1, m + 1, r);    //递归构造右儿子结点
		Pushup(k);    //更新父节点
	}
}

var Pushdown = function(k) {    //更新子树的lazy值，这里是RMQ的函数，要实现区间和等则需要修改函数内容
	if (lazy[k]) {    //如果有lazy标记
		lazy[k << 1] += lazy[k];    //更新左子树的lazy值
		lazy[k << 1 | 1] += lazy[k];    //更新右子树的lazy值
		t[k << 1] += lazy[k];        //左子树的最值加上lazy值
		t[k << 1 | 1] += lazy[k];    //右子树的最值加上lazy值
		lazy[k] = 0;    //lazy值归0
	}
}

//递归更新区间 updata(L,R,v,1,n,1);
//v是待增加的值, 可能是个负的!
var update = function(L, R, v, l, r, k) {    //[L,R]即为要更新的区间，l，r为结点区间，k为结点下标
	if (L <= l && r <= R) {    //如果当前结点的区间真包含于要更新的区间内
		lazy[k] += v;    //懒惰标记
		t[k] += v;    //最大值加上v之后，此区间的最大值也肯定是加v
	}
	else {
		Pushdown(k);    //重难点，查询lazy标记，更新子树
		var m = l + ((r - l) >> 1);
		if (L <= m)    //如果左子树和需要更新的区间交集非空
			update(L, R, v, l, m, k << 1);
		if (m < R)    //如果右子树和需要更新的区间交集非空
			update(L, R, v, m + 1, r, k << 1 | 1);
		Pushup(k);    //更新父节点
	}
}

//递归方式区间查询 query(L,R,1,n,1);
var query = function(L, R, l, r, k) {    //[L,R]即为要查询的区间，l，r为结点区间，k为结点下标
	if (L <= l && r <= R)    //如果当前结点的区间真包含于要查询的区间内，则返回结点信息且不需要往下递归
		return t[k];
	else {
		Pushdown(k);    /**每次都需要更新子树的Lazy标记*/
		var res = 0;    //返回值变量，根据具体线段树查询的什么而自定义
		var m = l + ((r - l) >> 1);    //m则为中间点，左儿子的结点区间为[l,m],右儿子的结点区间为[m+1,r]
		if (L <= m)    //如果左子树和需要查询的区间交集非空
			res += query(L, R, l, m, k << 1);
		if (R > m)    //如果右子树和需要查询的区间交集非空，注意这里不是else if，因为查询区间可能同时和左右区间都有交集
			res += query(L, R, m + 1, r, k << 1 | 1);
		return res;    //返回当前结点得到的信息
	}
}

/**
 * @param {[]number]} initArray
 * @return {}
 */
var init = function(initArray) {
	var nSize = initArray.length;
	if (nSize == 0) return;
	raw = initArray;
	raw.unshift(-99999);  //相当于移动下标
	t.fill(0);
	lazy.fill(0);
	rIndex = initArray.length;
	
	build(1, 1, rIndex);
};

/**
 * @param {number, number} index, value
 * @return {}
 */
var update = function(index, value) {
    var old = query(index + 1, index + 1, 1, rIndex, 1);
	update(index + 1, index + 1, value - old, 1, rIndex, 1);
};

/**
 * @param {number, number} startIndex, endIndex, 闭区间[startIndex, endIndex]
 * @return {number, int type}
 */
var sum = function(startIndex, endIndex) {
	return query(startIndex + 1, endIndex + 1, 1, rIndex, 1);
};

//runtest_js.js

//以下为main函数部分
/**
 * @param {string[]} arguments
 * @return {int}
 *
 *

 * note: must input with exactedly three para, first is testID
 *    second must be a name with full directory, eq: testproblems/sort/, third is type: "0": one custom testcase, "1": many testcases
 * return code
 *        0x30: OK; 
 *        0x31: para error;
 *        0x32: input file not occured;
 *        0x33: input data not valid/inputSubmit.txt or outputSubmit.txt file not valid or their lines number not matched; 
 *        0x34: output file cannot saved;
 *        0x35: wrong answer
 *        0x36: debug
 *        0x01: node exception return error
 *        the other codes are not predifined, if have other normal exit with exitcode(program normal exit by inside fuction runtime error!, if occur, tell me), in common condition, YOU SHOULD NOT use these not predifined codes here, otherwise confusing question may occur!

 */
var main = function(arguments) {
	//console.log(arguments.length);
	//console.log(arguments);
	
	if (arguments.length != 3){
		return 0x31;
	}else{
		var testId = arguments[0];     //
		var directory = arguments[1];
		var choose = arguments[2];
		var tempOut = "";
		
		if (choose != "0" && choose != "1"){
			return 0x31;
		}
		
		if (choose == "0"){
			var absInputPath = path.resolve(__dirname, testId + "input.txt");
			var readret = read_file_to_string(absInputPath);
			if (readret == null){
				return 0x32;
			}
			
			//console.log(readret);
			
			var absOutputPath = path.resolve(__dirname, testId + "output.txt");
			write_string_to_file_appendOrTruncated(absOutputPath, readret[0], false);
			
			var total = JSON.parse(readret[0]);  //shoule be number[]
			var paras = JSON.parse(readret[1]);  //shoule be number[]
			
			var out = [];
			if (Object.prototype.toString.call(total) === '[object Array]' && 
				Object.prototype.toString.call(paras) === '[object Array]'
				) {  //here only check they are both xxx[] (Array)
				init(total);
				for (var i = 0; i < paras.length; i += 3){
					var op = paras[i + 0];
					if (op == 1){
						update(paras[i + 1], paras[i + 2]);
					}
					else{
						out.push(sum(paras[i + 1], paras[i + 2]));
					}
				}
			}
			else{
				return 0x33;
			}
			
			JSONres = JSON.stringify(out);
			write_string_to_file_appendOrTruncated(absOutputPath, "\ncustomtest&" + JSONres + "&no compare res:(, not implemented yet", true);
			
			return 0x30;
		}
		else{
			var absInputSubmitPath = path.resolve(__dirname, "inputSubmit.txt");
			var absOutputSubmitPath = path.resolve(__dirname, "outputSubmit.txt");
			var inputret = read_file_to_string(absInputSubmitPath);
			var outret = read_file_to_string(absOutputSubmitPath);
			var absOutputPath = path.resolve(__dirname, testId + "output.txt");
			
			if (inputret == null || outret == null || inputret.length != 2*outret.length){
				return 0x32;
			}

			for (var line = 0; line < outret.length; line++){				
				write_string_to_file_appendOrTruncated(absOutputPath, inputret[1*line + 0], false);
				
				var total = JSON.parse(inputret[2*line + 0]);  //shoule be number[]
				var paras = JSON.parse(inputret[2*line + 1]);  //shoule be number[]
				
				var out = [];
				if (Object.prototype.toString.call(total) === '[object Array]' && 
					Object.prototype.toString.call(paras) === '[object Array]'
					) {  //here only check they are both xxx[] (Array)
					init(total);
					for (var i = 0; i < paras.length; i += 3){
						var op = paras[i + 0];
						if (op == 1){
							update(paras[i + 1], paras[i + 2]);
						}
						else{
							out.push(sum(paras[i + 1], paras[i + 2]));
						}
					}
				}
				else{
					return 0x33;
				}
				
				JSONres = JSON.stringify(out);
				
				if (JSONres != outret[line]){ //WA
					write_string_to_file_appendOrTruncated(absOutputPath, 
						"\ntestcase" + line.toString() + "&" + JSONres + "&" + outret[line], 
						true);
					return 0x35;
				}
			}
			
			write_string_to_file_appendOrTruncated(absOutputPath, 
				"AC\npassed all" + outret.length.toString() + " testcases &AC&AC", 
				false);
			return 0x30;
		}
	}
};

var arguments = process.argv.splice(2);
process.exit(main(arguments));